--[[
CastYeller2 - Announce important spell events.

Copyright (C) 2009 Adirelle

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
--]]

if not CastYeller2 then return end
local CastYeller2 = CastYeller2 
local L = CastYeller2.L

local mod, super = CastYeller2:NewSourceModule('Spellcasts', 'AceEvent-3.0')

mod.moduleTitle = L['Spellcasts']

mod.triggers = {
	instantCast = L['Successful instant spell casts'],
	startCasting = L['Start spell castings'],
	castFailure = L['Spell failures'],
	spellMiss = L['Spell misses'],
}

mod.defaultAnnounces = {
	instantCast = L['Cast $SPELL[ on ${TARGET}[ ${RAIDICONTEXTURE}]]'],
	startCasting = L['Casting $SPELL[ on ${TARGET}[ ${RAIDICONTEXTURE}]]'],
	castFailure = L['$SPELL[ on ${TARGET}[ ${RAIDICONTEXTURE}]] failed[: ${REASON}]'],
	spellMiss = L['$SPELL missed[ ${TARGET}[ ${RAIDICONTEXTURE}]][: ${REASON}]'],
}

mod.announceOptions = {
	targetting = {
		name = L['Targetting'],
		desc = L["Select the way the spell target is chosen for this announce. This setting should be used when CastYeller2 does not properly guess the target."],
		type = 'select',
		values = {
			none = L['No target'],
			standard = L['Standard'],
			self = L['Always self'],
		},
	},
}

mod.announceDefaultsDB = {
	targetting = 'standard',
}

-- Failure reasons that worth announcing
local failureFilter = {
	[SPELL_FAILED_NOT_READY] = "SPELL_FAILED_NOT_READY",
}
do
	local prefixes = { "SPELL_FAILED_", "ERR_SPELL_FAILED_", "ERR_", "ERR_SPELL_", "" }
	local suffixes = {
		"LINE_OF_SIGHT", "OUT_OF_RANGE", "STUNNED", "SILENCED", "FLEEING", "PACIFIED",
		"POSSESSED", "NOT_IN_CONTROL", "CHARMED", "CONFUSED", "AFFECTING_COMBAT",
		"TARGET_AFFECTING_COMBAT", "NOT_IN_COMBAT", "INTERRUPTED"
	}
	for i, prefix in ipairs(prefixes) do
		for j, suffix in ipairs(suffixes) do
			local name = prefix..suffix
			local msg = _G[name]
			if msg then
				failureFilter[msg] = name
			end
		end
	end
end

local pName = UnitName('player')
local throttle = {}
local failureThrottle = {}

function mod:OnEnable()
	super.OnEnable(self)
	self:RegisterEvent('UNIT_SPELLCAST_START')
	self:RegisterEvent('UNIT_SPELLCAST_CHANNEL_START', 'UNIT_SPELLCAST_START')
	self:RegisterEvent('UNIT_SPELLCAST_SUCCEEDED')
	self:RegisterEvent('UNIT_SPELLCAST_SENT')
	self:RegisterCombatListener('SPELL_CAST_FAILED')
	self:RegisterCombatListener('SPELL_MISSED')
	wipe(throttle)
	wipe(failureThrottle)
end

function mod:TriggerAnnounce(trigger, target, spell, ...)
	local now = GetTime()
	if spell and now > (throttle[spell] or 0) then
		super.TriggerAnnounce(self, trigger, target, spell, ...)
		throttle[spell] = now + 1.0
	end
end

function mod:BuildInfo(announce, trigger, target, spell, duration, spellId, targetGUID, targetFlags, ...)
	if announce.alwaysSelfCast then
		announce.targetting = "self"
		announce.alwaysSelfCast = nil
	end
	if announce.targetting == "none" then
		return super.BuildInfo(self, announce, trigger, nil, spell, duration, spellId)
	elseif announce.targetting == "self" then
		return super.BuildInfo(self, announce, trigger, pName, spell, duration, spellId, UnitGUID('player'), nil, 'raidIcon', GetRaidTargetIndex('player'), ...)
	else
		return super.BuildInfo(self, announce, trigger, target, spell, duration, spellId, targetGUID, targetFlags, ...)
	end
end

local function GuessRaidIcon(name)
	if UnitInRaid(name) or UnitInParty(name) then
		return GetRaidTargetIndex(name)
	elseif name == UnitName('target') then
		return GetRaidTargetIndex('target')
	elseif name == UnitName('focus') then
		return GetRaidTargetIndex('focus')
	elseif name == UnitName('mouseover') then
		return GetRaidTargetIndex('mouseover')
	end
end

local castings = {}
local targets = {}
local icons = {}

function mod:UNIT_SPELLCAST_SENT(_, unit, spell, _, target)
	if unit == 'player' and self:IsNameWatched(spell) then
		castings[spell], targets[spell], icons[spell] = true, target, GuessRaidIcon(target)
	end
end

function mod:UNIT_SPELLCAST_START(_, unit, spell)
	if unit == 'player' and castings[spell] then
		self:TriggerAnnounce('startCasting', targets[spell], spell, nil, nil, nil, nil, 'raidIcon', icons[spell])
	end
end

function mod:UNIT_SPELLCAST_SUCCEEDED(_, unit, spell, ...)
	if unit == 'player' and castings[spell] then
		self:TriggerAnnounce('instantCast', targets[spell], spell, nil, nil, nil, nil, 'raidIcon', icons[spell])
		castings[spell], targets[spell], icons[spell] = nil, nil, nil
	end
end

function mod:SPELL_CAST_FAILED(_, time, _, source, _, targetGUID, target, targetFlags, spellId, spell, _, reason)
	if source == pName and self:IsNameWatched(spell) and failureFilter[reason] then
		local throttleKey = spell..reason
		if failureFilter[reason] == "SPELL_FAILED_NOT_READY" then
			local start, duration, enabled = GetSpellCooldown(spell)
			if start and (duration or 0) < 1.5 then	
				failureThrottle[throttleKey] = start + duration
				return
			end
		end
		if time > (failureThrottle[throttleKey] or 0) then
			self:TriggerAnnounce('castFailure', target, spell, nil, spellId, targetGUID, targetFlags, 'reason', reason)
			failureThrottle[throttleKey] = time + 10
		end
		castings[spell], targets[spell], icons[spell] = nil, nil, nil
	end
end

function mod:SPELL_MISSED(_, _, _, source, _, targetGUID, target, targetFlags, spellId, spell, _, reason)
	if source == pName and self:IsNameWatched(spell) and reason ~= 'ABSORB' then
		self:TriggerAnnounce('spellMiss', target, spell, nil, spellId, targetGUID, targetFlags, 'reason', reason and _G[reason] or reason)
		castings[spell], targets[spell], icons[spell] = nil, nil, nil
	end
end

